import {elements} from "../elements.js";
import {constants} from "../constants.js";
import {terrariaMap} from "../terrariaMap.js";

const {context, canvas} = elements

export class Character {
  height = 0
  width = 0
  g = 0
  /**
   * 横向移动速度
   * @type {number}
   */
  xv = 0
  /**
   * 纵向移动速度
   * @type {number}
   */
  yv = 0
  /**
   * 默认跳跃速度
   * @type {number}
   */
  jumpV = -365
  maxYv = 700
  /**
   * 默认横向移动速度
   * @type {number}
   */
  parallelV = 140
  x = 0
  y = 0
  img

  parallelOrder = 0
  jumpOrder = false

  constructor(height, width, g, img) {
    this.g = g
    this.height = height
    this.width = width
    this.img = img
    this.x = 0
    this.y = canvas.height - this.height
  }

  parallelMove = (pos) => {
    this.parallelOrder = pos
  }

  cancelParallelMove = (pos) => {
    if (this.parallelOrder === pos) {
      this.parallelOrder = 0
    }
  }

  isFlying = () => {
    if(this.y % 10 !== 0) {
      return true
    }
    if (this.y < canvas.height - this.height) {
      let blockY = (this.y + this.height) / 10
      let blockX = Math.floor(this.x / constants.CUBE_LENGTH)
      let jm = this.x % 10===0?this.width / 10 : this.width /10 + 1
      for (let j = 0; j < jm; j++) {
        // 在下一个位置上有土块
        if (terrariaMap.getCode(blockX + j, blockY)) {
          return false
        }
      }
      return true
    }
    return false
  }

  jump = () => {
    this.jumpOrder = true
  }

  cancelJump = () => {
    this.jumpOrder = false
  }

  #calcParallelSpeed = () => {
    this.xv = this.parallelOrder * this.parallelV
  }

  #calcCrossSpeed = () => {
    // 腾空状态，计算自由落体速度
    if (this.isFlying()) {
      let exceptYv = this.yv + this.g * constants.FRAME_TIME
      if (exceptYv > 0){
        exceptYv = Math.min(exceptYv,this.maxYv)
      }else{
        exceptYv = Math.max(exceptYv,-this.maxYv)
      }
      this.yv = exceptYv
      return
    }
    // 落地，有跳跃指令
    if (this.jumpOrder) {
      this.yv = this.jumpV
      return;
    }
    // 落地，且没有跳跃指令
    this.yv = 0
  }

  /**
   * 计算期望位移
   * @returns {{x: number, y: number}}
   */
  #calcExpectDisplacement = () => {
    return {x: this.xv * constants.FRAME_TIME, y: this.yv * constants.FRAME_TIME}
  }

  /**
   * 计算实际位置
   * @param expectDisplacement {{x:number,y:number}} 期望位移
   */

  #calcActualPosition = (expectDisplacement) => {
    let {y} = expectDisplacement
    // 计算横向真实位置
    let {blocked,direction}=this.#calcActualX(expectDisplacement.x)
    if (blocked && y === 0 && Character.#justOneBottomBlock(this.x / 10,this.y / 10 , this.height / 10 ,this.width / 10 ,direction )){
      this.y -= 10
      return
    }
    // 计算纵向真实位置
    this.#calcActualY(expectDisplacement.y)
  }

  static #hasBlocks(x, y, height, width) {
    for (let i = x; i < x + width; i++) {
      for (let j = y; j < y + height; j++) {
        if (terrariaMap.getCode(i, j) !== 0) {
          return true
        }
      }
    }
    return false
  }

  static #justOneBottomBlock(x, y, height,width,direction) {
    for (let i = 0 ; i < width ; i++){
      if (terrariaMap.getCode(x+i,y) !== 0){
        return false
      }
    }
    let i = direction ? x+width : x-1
    for (let j = 0; j <  height; j++) {
      if (j === height - 1){
        return terrariaMap.getCode(i,j + y) !== 0
      }
      if (terrariaMap.getCode(i, j + y) !== 0) {
        return false
      }
    }
  }


  /**
   * 计算横向位置
   * @param disX {int} 期望位移
   * @returns {{blocked:boolean,direction:boolean}}
   */
  #calcActualX = (disX) => {
    if (disX === 0) {
      return {blocked:false,direction:false}
    }

    if (this.x % 10 !== 0){
      // 移动确实太小，完全不考虑跨格子的问题
      if (disX + (this.x) % 10 < 10 && disX + (this.x) % 10 >= 0){
        this.x += disX
        return {blocked:false,direction:false}
      }

      // 贴边
      if (disX > 0) {
        let rest = (this.x + disX) % 10
        this.x = this.x + disX - rest
        disX = rest
      } else {
        let rest = this.x % 10
        this.x -= rest
        disX += rest
      }
    }

    let nextBlockX
    if(disX > 0){
      nextBlockX = this.x / 10 + this.width / 10
    }else{
      nextBlockX = (this.x / 10) - 1
    }

    // 找到头顶所在的格子
    let startY = Math.floor(this.y / 10)
    // 如果不是整数，则需要多判断一格（脚下）
    let height = this.y % 10 === 0 ? this.height / 10  : this.height / 10 + 1
    while (true) {
      // 在接下来的一列是有格子的
      let hasBlock = Character.#hasBlocks(nextBlockX, startY, height, 1)
      if (hasBlock) {
        // 不动了，直接return
        return {blocked:true,direction:disX > 0}
      } else {
        // 下一个位置没有格子

        // 如果剩余的disX太大，则需要继续向前遍历
        if (disX > 10 || disX < -10) {
          if (disX > 0) {
            this.x += 10
            disX -= 10
            nextBlockX++
          } else {
            this.x -= 10
            disX += 10
            nextBlockX--
          }
        } else {
          //如果disX绝对值小于10，则直接位移
          this.x += disX
          return {blocked:false,direction:false}
        }
      }
    }

  }

  /**
   * 计算纵向位置
   * @param disY {int} 期望位移
   */
  #calcActualY = (disY) => {
    if (disY === 0){
      return;
    }

    // 触底
    // TODO
    if (this.y + disY > canvas.height - this.height) {
      // 需要将纵向速度归零
      this.yv = 0
      this.y = canvas.height - this.height
      return;
    }

    if(this.y % 10 !== 0){
      // 移动确实太小，完全不考虑跨格子的问题
      if (disY + this.y % 10 < 10 && disY + this.y % 10 >= 0){
        this.y += disY
        return
      }

      // 贴边
      if (disY > 0){
        let rest = (this.y + disY) % 10
        this.y = this.y + disY - rest
        disY -= rest
      }else{
        let rest = this.y % 10
        this.y -= rest
        disY += rest
      }
    }

    let nextBlockY
    if (disY > 0) {
      nextBlockY = (this.y + this.height) / 10
    } else {
      nextBlockY = this.y / 10 - 1
    }

    let startX = Math.floor(this.x / 10)
    let width = this.x % 10 === 0 ? this.width / 10 : this.width / 10 + 1

    while (true){
      let hasBlock = Character.#hasBlocks(startX,nextBlockY,1,width)
      if (hasBlock){
        // 记得速度归零
        this.yv = 0
        return;
      }else {
        if (disY > 10 || disY <= -10){
          if (disY > 0){
            this.y += 10
            disY -= 10
            nextBlockY++
          }else {
            this.y -= 10
            disY += 10
            nextBlockY--
          }
        }else{
          this.y+=disY
          return;
        }
      }
    }

  }

  move = () => {
    this.#calcCrossSpeed()
    this.#calcParallelSpeed()
    let expectDisplacement = this.#calcExpectDisplacement()
    this.#calcActualPosition(expectDisplacement)
    this.draw()
  }

  draw = () => {
    context.drawImage(this.img, this.x, this.y);
  }


}